package net.wangds.auth.user.ctrl;

import net.wangds.utils.JsonUtils;
import net.wangds.auth.account.comp.AccountComp;
import net.wangds.auth.account.entity.GeneralAccountEntity;
import net.wangds.auth.department.comp.GeneralDepartmentComp;
import net.wangds.auth.grant.entity.GeneralGrantEntity;
import net.wangds.auth.grant.service.GrantService;
import net.wangds.auth.job.comp.GeneralJobComp;
import net.wangds.auth.job.entity.GeneralJobUserRef;
import net.wangds.auth.module.Account;
import net.wangds.auth.module.Grant;
import net.wangds.auth.module.ResourceSchemaEnum;
import net.wangds.auth.module.Role;
import net.wangds.auth.resource.comp.GeneralResourceComp;
import net.wangds.auth.role.comp.GeneralRoleComp;
import net.wangds.auth.user.comp.GeneralUserComp;
import net.wangds.auth.user.entity.GeneralUserEntity;
import net.wangds.common.base.module.Holder;
import net.wangds.jpa.Sorts;
import net.wangds.jpa.comp.AbstractRemovedComp;
import net.wangds.jpa.convertor.Page2MultipleRespConv;
import net.wangds.jpa.convertor.PagingForm2PageRequestConv;
import net.wangds.jpa.ctrl.AbstractJpaCompCtrl;
import net.wangds.log.helper.LogHelper;
import net.wangds.web.entrypoint.BaseMultipleResp;
import net.wangds.web.entrypoint.BaseResp;
import net.wangds.web.entrypoint.PagingForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;

import javax.transaction.Transactional;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/auth/user")
public class UserCtrl extends AbstractJpaCompCtrl<GeneralUserEntity> {


    @Autowired
    private GeneralUserComp mmCmpUsr;


    @Autowired
    private GeneralDepartmentComp mmCmpDept;

    @Autowired
    private GeneralJobComp mmCmpJob;


    @Autowired
    private GrantService mmServGrant;

    @Autowired
    private GeneralResourceComp mmCmpRes;

    @Autowired
    private AccountComp<? extends Account> mmCmpAccount;

    @Autowired
    private GeneralRoleComp mmCmpRole;


    private PagingForm2PageRequestConv mmCovPaging2Pr = new PagingForm2PageRequestConv(()->Sorts.createTm_desc);

    @Autowired
    private Page2MultipleRespConv<Role> mmCovPg2Mr;


    @Override
    protected AbstractRemovedComp<GeneralUserEntity> fetchJpaComp() {
        return mmCmpUsr;
    }

    @Override
    protected BaseMultipleResp<GeneralUserEntity> listWithKeywords(PagingForm page) {
        return mmConvPage2Resp.convert(mmCmpUsr.findByKeywords(page.getKeywords(), mmConvPf2Pr.convert(page)));
    }

    @RequestMapping(value = "/bindEmail/{accountId}/{email}", method = RequestMethod.PUT)
    public void bindEmailUser(@PathVariable("accountId") String accountId,
                              @PathVariable("email") String email) {
        mmCmpAccount.findById(accountId).ifPresent(acc -> mmCmpUsr.createEmailUser(acc, email));
    }

    @RequestMapping(value = "/bindPhone/{accountId}/{phone}", method = RequestMethod.PUT)
    public void bindSmsUser(@PathVariable("accountId") String accountId,
                            @PathVariable("phone") String phone) {
        mmCmpAccount.findById(accountId).ifPresent(acc -> mmCmpUsr.createSmsUser(acc, phone));
    }

    @Transactional
    @RequestMapping(value = "/listGrant/{userId}", method = RequestMethod.POST)
    public BaseResp<List<? extends Grant>> listGrants(
            @PathVariable("userId") String userId) {

        BaseResp<List<? extends Grant>> res = BaseResp.of(null);
        mmCmpUsr.findById(userId).ifPresent(user -> {
            res.body(mmServGrant.listGrantedByOwnerId(userId));
        });

        return res;
    }

    @Transactional
    @RequestMapping(value = "/grantRes/{userId}/{resId}/{privilege}", method = RequestMethod.POST)
    public BaseResp<Boolean> grantRes2User(
            @PathVariable("userId") String userId,
            @PathVariable("resId") String resId,
            @PathVariable("privilege") int privilege) {

        mmCmpUsr.findById(userId).ifPresent(user -> {
            mmCmpRes.findById(resId).ifPresent(res -> {
                mmServGrant.grant(res, user, privilege);
            });
        });

        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/ungrantRes/{userId}/{resId}/{privilege}", method = RequestMethod.POST)
    public BaseResp<Boolean> ungrantRes2User(
            @PathVariable("userId") String roleId,
            @PathVariable("resId") String resId,
            @PathVariable("privilege") int privilege
    ) {

        mmCmpUsr.findById(roleId).ifPresent(user -> {
            mmCmpRes.findById(resId).ifPresent(res -> {
                mmServGrant.ungrant(res, user, privilege);
            });
        });

        return BaseResp.of(true);
    }


    @Transactional
    @RequestMapping(value = "/bindAccount/{userId}/{accountId}", method = RequestMethod.POST)
    public BaseResp<Boolean> bindAccount(
            @PathVariable("userId") String uid,
            @PathVariable("accountId") String accountId) {
        LogHelper.dev(() -> "bindAccess");
        mmCmpUsr.findById(uid).ifPresent(user -> mmCmpUsr.ref2Account(user, GeneralAccountEntity.of(accountId)));

        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/unbindAccount/{userId}/{accountId}", method = RequestMethod.POST)
    public BaseResp<Boolean> unbindAccount(
            @PathVariable("userId") String roleId,
            @PathVariable("accountId") String accountId
    ) {

        mmCmpUsr.findById(roleId).ifPresent(user ->
                mmCmpUsr.unref2Account(user, GeneralAccountEntity.of(accountId)));

        return BaseResp.of(true);
    }


    @Transactional
    @RequestMapping(value = "/bindDept/{userId}/{deptId}", method = RequestMethod.POST)
    public BaseResp<Boolean> bindDept(
            @PathVariable("userId") String uid,
            @PathVariable("deptId") String deptId) {
        LogHelper.dev(() -> "bindAccess");
        mmCmpUsr.findById(uid).ifPresent(user ->
                mmCmpDept.findById(deptId).ifPresent(dept ->
                        mmCmpUsr.ref2Department(user, dept)
                ));
        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/unbindDept/{userId}/{deptId}", method = RequestMethod.POST)
    public BaseResp<Boolean> unbindDept(
            @PathVariable("userId") String uid,
            @PathVariable("deptId") String deptId
    ) {

        mmCmpUsr.findById(uid).ifPresent(user ->
                mmCmpDept.findById(deptId).ifPresent(dept ->
                        mmCmpUsr.unref2Department(user, dept)
                ));

        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/bindJob/{userId}/{jobId}", method = RequestMethod.POST)
    public BaseResp<Boolean> bindJob(
            @PathVariable("userId") String uid,
            @PathVariable("jobId") String jobId) {
        mmCmpUsr.findById(uid).ifPresent(user ->
                mmCmpJob.findById(jobId).ifPresent(job ->
                        mmCmpUsr.ref2Job(user, job)
                ));
        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/unbindJob/{userId}/{jobId}", method = RequestMethod.POST)
    public BaseResp<Boolean> unbindJob(
            @PathVariable("userId") String uid,
            @PathVariable("jobId") String jobId
    ) {

        mmCmpUsr.findById(uid).ifPresent(user ->
                mmCmpJob.findById(jobId).ifPresent(job ->
                        mmCmpUsr.unref2Job(user, job)
                ));

        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/bindRole/{userId}/{roleId}", method = RequestMethod.POST)
    public BaseResp<Boolean> bindRole(
            @PathVariable("userId") String uid,
            @PathVariable("roleId") String roleId) {
        mmCmpUsr.findById(uid).ifPresent(user ->
                mmCmpRole.findById(roleId).ifPresent(role ->
                        mmCmpUsr.ref2Role(user, role)
                ));
        return BaseResp.of(true);
    }

    @Transactional
    @RequestMapping(value = "/unbindRole/{userId}/{roleId}", method = RequestMethod.POST)
    public BaseResp<Boolean> unbindRole(
            @PathVariable("userId") String uid,
            @PathVariable("roleId") String roleId
    ) {

        mmCmpUsr.findById(uid).ifPresent(user ->
                mmCmpRole.findById(roleId).ifPresent(job ->
                        mmCmpUsr.unref2Role(user, job)
                ));

        return BaseResp.of(true);
    }


    @Transactional
    @RequestMapping(value = "/{userId}/deptIds", method = RequestMethod.POST)
    public BaseResp<? extends Collection<String>> loadUserDeptIds(
            @PathVariable("userId") String uid) {
        BaseResp res = BaseResp.of(null);
        mmCmpUsr.findById(uid).ifPresent(user ->
                        res.body(mmCmpUsr.findDepartmentUserRef(user).stream().map(c->c.getDeptId()).collect(Collectors.toList()))
                );
        return res;
    }

    @Transactional
    @RequestMapping(value = "/{userId}/jobIds", method = RequestMethod.POST)
    public BaseResp<? extends Collection<String>> loadUserJobIds(
            @PathVariable("userId") String uid) {
        BaseResp res = BaseResp.of(null);
        mmCmpUsr.findById(uid).ifPresent(user ->
                res.body(mmCmpUsr.findJobUserRef(user).stream().map(GeneralJobUserRef::getJobId).collect(Collectors.toList())));
        return res;
    }



    @RequestMapping(value="/{id}/binded/roles", method = RequestMethod.POST)
    BaseMultipleResp<Role> listBindedRoles(
            @PathVariable("id") String uid
    ){
        BaseMultipleResp<Role> res = BaseMultipleResp.list(Collections.emptyList());
        mmCmpUsr.findById(uid).ifPresent(usr->{
            res.fill(mmCmpRole.listByRefUser(usr));
        });

        LogHelper.dev("binded:"+JsonUtils.toJsonStr(res));
        return res;
    }

    @RequestMapping(value="/{id}/unbinded/roles", method = RequestMethod.POST)
    BaseMultipleResp<Role> listUnbindedRoles(
            @PathVariable("id") String uid
            , @RequestBody PagingForm paging
    ){
        Holder<Page<Role>> h = Holder.of(null);
        mmCmpUsr.findById(uid).ifPresent(usr->{
            h.target = mmCmpRole.listUserUnrefRoles(usr, mmCovPaging2Pr.convert(paging));
        });
        LogHelper.dev("unbinded:"+ JsonUtils.toJsonStr(h.target));
        return mmCovPg2Mr.convert(h.target);
    }

    @Transactional
    @RequestMapping(value = "/{userId}/menuIds", method = RequestMethod.POST)
    public BaseResp<? extends Collection<String>> loadUserMenuIds(
            @PathVariable("userId") String uid) {
        BaseResp res = BaseResp.of(null);
        mmCmpUsr.findById(uid).ifPresent(user ->
                res.body(mmServGrant.listByOwnerIdAndSchemaAndAction(user.getId(), ResourceSchemaEnum.MENU, 15).stream().map(GeneralGrantEntity::getResId).collect(Collectors.toList())));
        return res;
    }


}